// 引用与借用
// 这些 & 符号就是 引用，它们允许你使用值但不获取其所有权。引用默认是不可变的。我们无法通过引用修改内容
// 与使用 & 引用相反的操作是 解引用（dereferencing），它使用解引用运算符 *
// 我们将创建一个引用的行为称为 借用（borrowing）。
// 正如现实生活中，如果一个人拥有某样东西，你可以从他那里借来。当你使用完毕，必须还回去。
// 在任意给定时间，要么 只能有一个可变引用，要么 只能有多个不可变引用
/// 当我们只读变量时，推荐 &s1 使用不可变（只读）引用。如果要修改变量，推荐 &mut s1 可变（读写）引用。不要多申请权限

// 引用
fn references_1() {
    let s1 = String::from("hello");

    // &s1 语法让我们创建一个 指向 值 s1 的引用，但是并不拥有它。
    // 因为并不拥有这个值，所以当引用停止使用时，它所指向的值也不会被丢弃。
    let len = calculate_length(&s1);

    // 如下的行为就是借用
    let r1 = &s1;

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}



// 可变引用
// 可变引用有一个很大的限制：在同一时间（同一作用域内），只能有一个对某一特定数据的可变引用
fn references_2() {
    let mut s = String::from("hello");

    change(&mut s);

    // 可变引用在同一时间只能借出一份。下面的行为就是借用，创建了一个可变借用
    let r1 = &mut s;    // 第一次通过
    // let r2 = &mut s;    // 第二次不通过，编译报错

    // println!("{}, {}", r1, r2);

}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

// 可以使用大括号来创建一个新的作用域，以允许拥有多个可变引用，只是不能 同时 拥有
fn references_3() {
    let mut s = String::from("hello");

    {
        let r1 = &mut s;
    } // r1 在这里离开了作用域，所以我们完全可以创建一个新的引用

    let r2 = &mut s;
}


// 对于可变变量，允许在同一作用域内存在多个引用，因为默认引用是只读的
// 但不允许同时存在不可变引用和可变引用，因为可变引用的修改将会导致不可变引用的值一并发生变化，从而产生前后两次数据不一致的幻读
pub fn references_4() {
    let mut s = String::from("hello");

    let r1 = &s; // 没问题
    let r2 = &s; // 没问题
    println!("{} and {}", r1, r2);
    // let r3 = &mut s; // 大问题

    // println!("{}, {}, and {}", r1, r2, r3);
}

// 借用规则，当拥有某值的不可变引用时，就不能再获取一个可变引用，或去直接改变原始可变变量
fn references_5() {
    let mut s = String::from("hello world");

    // 使用了可变引用，但返回值还没有失效
    let word = first_word(&s);

    // 如果此时（在同一作用域内）修改原始可变变量直接会导致所有的引用也发生变化
    s.clear(); // error!

    println!("the first word is: {}", word);
}

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[..i]
        }
    }
    &s[..]
}